/*********************************************************************************************************
 *  ------------------------------------------------------------------------------------------------------
 *  file description
 *  ------------------------------------------------------------------------------------------------------
 *         \file  stack.h
 *        \brief  This is a general-purpose C language stack module, with circular mode and common data structure
 *       \author  Lamdonn
 *        \email  Lamdonn@163.com
 *      \version  1.1.0
 *      \license  GPL2
 *    \copyright  Copyright (C) 2023 Lamdonn.
 ********************************************************************************************************/
#ifndef __stack_H
#define __stack_H

#include "deque.h"

/* stack type define */
typedef struct
{
    DEQUE parent;                       /* parent object */
} STACK, *stack_t;

/* stack orgin define */
#define STACK_ORGIN_BOTTOM              DEQUE_ORGIN_FRONT
#define STACK_ORGIN_TOP                 DEQUE_ORGIN_BACK

/* control mode */
#define STACK_MODE_DEFAULT              DEQUE_MODE_DEFAULT      /* default mode */
#define STACK_MODE_READ                 DEQUE_MODE_READ         /* readable mode */
#define STACK_MODE_MODIFY               DEQUE_MODE_MODIFY       /* modifiable mode */
#define STACK_MODE_OVERWRITE            DEQUE_MODE_OVERWRITE    /* overwrite mode, when the stack is full, the new data overwrites the old data */
#define STACK_MODE_POP_RESET            DEQUE_MODE_POP_RESET    /* pop data and reset item */
#define STACK_MODE_POP_HOLD             DEQUE_MODE_POP_HOLD     /* pop data but not delete */
#define STACK_MODE_LMT_PUSH             DEQUE_MODE_LMT_PUSH     /* limit push */
#define STACK_MODE_LMT_POP              DEQUE_MODE_LMT_POP      /* limit pop */
#define STACK_MODE_RESIZE               DEQUE_MODE_RESIZE       /* resize */
#define STACK_MODE_DROP_BOTTOM          DEQUE_MODE_DROP_FRONT   /* when reducing the stack, the front is lost first */

/* set way */
#define STACK_SET_MODE                  DEQUE_SET_MODE
#define STACK_RESET_MODE                DEQUE_RESET_MODE

/** 
 *  \brief initialize stack
 *  \param[in] stack: stack handler
 *  \param[in] array: address of array
 *  \param[in] dsize: size of stack items
 *  \param[in] capacity: capacity of stack
 *  \return 1: success or 0: fail
 */
int stack_init(stack_t stack, void* array, int dsize, int capacity);

/** 
 *  \brief release stack
 *  \param[in] stack: stack handler
 *  \return none
 */
void stack_deinit(stack_t stack);

/** 
 *  \brief create stack
 *  \param[in] dsize: size of stack items
 *  \param[in] capacity: capacity of stack
 *  \return stack handler or NULL: fail
 */
stack_t stack_create(int dsize, int capacity);

/** 
 *  \brief delete stack created by stack_create function
 *  \param[in] stack: stack handler
 *  \return none
 */
void stack_delete(stack_t stack);

/** 
 *  \brief judge whether the stack is empty
 *  \param[in] stack: stack handler
 *  \return 1: empty or 0: not empty
 */
int stack_empty(stack_t stack);

/** 
 *  \brief check whether the stack is full
 *  \param[in] stack: stack handler
 *  \return 1: full or 0: not full
 */
int stack_full(stack_t stack);

/** 
 *  \brief get current items size
 *  \param[in] stack: stack handler
 *  \return size of item
 */
int stack_size(stack_t stack);

/** 
 *  \brief push data into stack
 *  \param[in] stack: stack handler
 *  \param[in] data: address of stack item data
 *  \return 1: success or 0: fail
 */
int stack_push(stack_t stack, void* data);

/** 
 *  \brief push data from stack
 *  \param[in] stack: stack handler
 *  \param[out] data: address of stack item data
 *  \return 1: success or 0: fail
 */
int stack_pop(stack_t stack, void* data);

/** 
 *  \brief clean stack
 *  \param[in] stack: stack handler
 *  \return none
 */
void stack_clear(stack_t stack);

/** 
 *  \brief get the pointer of stack's item
 *  \param[in] stack: stack handler
 *  \param[in] index: stack index
 *  \param[in] orgin: STACK_ORGIN_BOTTOM or STACK_ORGIN_TOP
 *  \return pointer of stack's item
 */
void* stack_data(stack_t stack, int index, int orgin);

/** 
 *  \brief insert data to stack
 *  \param[in] stack: stack handler
 *  \param[in] index: stack index
 *  \param[in] data: address of stack item data
 *  \param[in] orgin: STACK_ORGIN_BOTTOM or STACK_ORGIN_TOP
 *  \return 1: success or 0: fail
 */
int stack_insert(stack_t stack, int index, void* data, int orgin);

/** 
 *  \brief erase items from the stack
 *  \param[in] stack: stack handler
 *  \param[in] begin: begin index of stack
 *  \param[in] end: end index of stack
 *  \param[in] orgin: STACK_ORGIN_BOTTOM or STACK_ORGIN_TOP
 *  \return 1: success or 0: fail
 */
int stack_erase(stack_t stack, int begin, int end, int orgin);

/** 
 *  \brief read the data of stack
 *  \param[in] stack: stack handler
 *  \param[in] index: stack index
 *  \param[out] data: address of stack item data
 *  \param[in] orgin: STACK_ORGIN_BOTTOM or STACK_ORGIN_TOP
 *  \return 1: success or 0: fail
 */
int stack_read(stack_t stack, int index, void* data, int orgin);

/** 
 *  \brief modify the data of stack
 *  \param[in] stack: stack handler
 *  \param[in] index: stack index
 *  \param[in] data: address of stack item data
 *  \param[in] orgin: STACK_ORGIN_BOTTOM or STACK_ORGIN_TOP
 *  \return 1: success or 0: fail
 */
int stack_modify(stack_t stack, int index, void* data, int orgin);

/** 
 *  \brief get stack capacity
 *  \param[in] stack: stack handler
 *  \return the capacity of the stack
 */
int stack_capacity(stack_t stack);

/** 
 *  \brief resize the stack
 *  \param[in] stack: stack handler
 *  \param[in] capacity: new capacity of stack
 *  \return 1: success or 0: fail
 */
int stack_alter_capacity(stack_t stack, int capacity);

/** 
 *  \brief modify stack's mode
 *  \param[in] stack: stack handler
 *  \param[in] mode: stack mode
 *  \param[in] set: STACK_SET_MODE or STACK_RESET_MODE
 *  \return none
 */
void stack_mode(stack_t stack, int mode, int set);

/** stack
 *  \brief macro definition method for creating stack
 *  \param[in] type: tpye of item(e.g int char)
 *  \param[in] capacity: capacity of stack
 *  \return stack handler
 */
#define stack(type, capacity)           (stack_create(sizeof(type),(capacity)))

/** 
 *  \brief define a const stack, end use does not need to be deleted
 *  \param[in] type: tpye of item(e.g int char)
 *  \param[in] capacity: capacity of stack
 *  \return const stack
 */
#define const_stack(type, capacity)     (stack_t)(&(DEQUE){{3,(type[capacity]){},sizeof(type),0},capacity,0,0,0})

/** 
 *  \brief macro definition method for delete stack
 *  \param[in] stack: stack handler
 *  \return none
 */
#define _stack(stack)                   do{stack_delete(stack);(stack)=NULL;}while(0)

/** 
 *  \brief macro definition method for get bottom item of stack
 *  \param[in] stack: stack handler
 *  \param[in] type: tpye of item
 *  \param[in] i: index
 *  \return reference to the specified item
 */
#define stack_at(stack, type, i)        (*(type *)stack_data((stack),(i),STACK_ORGIN_BOTTOM))

/** 
 *  \brief macro definition method for get bottom item of stack
 *  \param[in] stack: stack handler
 *  \param[in] type: tpye of item
 *  \param[in] i: index
 *  \return reference to the specified item
 */
#define stack_bottom(stack, type, i)    (*(type *)stack_data((stack),(i),STACK_ORGIN_BOTTOM))

/** 
 *  \brief macro definition method for get top item of stack
 *  \param[in] stack: stack handler
 *  \param[in] type: tpye of item
 *  \param[in] i: index
 *  \return reference to the specified item
 */
#define stack_top(stack, type, i)       (*(type *)stack_data((stack),(i),STACK_ORGIN_TOP))    

#endif
